﻿using Abp;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Json;
using Abp.Linq.Extensions;
using Abp.Timing;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.WorkFlows.Archive.Dto;
using Mt.Site.Application.WorkFlows.Dto.Manager;
using Mt.Site.Core.Organizations.UserCache.Models;
using Mt.Site.Core.WorkFlows;
using Mt.Site.Core.WorkFlows.Design;
using Mt.Site.Core.WorkFlows.Design.Entities;
using Mt.Site.Core.WorkFlows.Permissions;
using Mt.Site.Core.WorkFlows.Process;
using Mt.Site.Core.WorkFlows.Process.Entities;
using Mt.Site.Core.WorkFlows.Process.Models;
using Mt.Site.Core.WorkFlows.Process.Stores;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Application.Archive
{
    [AbpAuthorize(WorkFlowPermissions.Pages_Manage_WorkFlow)]
    public class ArchiveAppService : ApplicationService, IArchiveAppService
    {
        private readonly IRepository<WorkFlowPaperArchive, Guid> _workFlowPaperArchiveRepository;
        private readonly IWorkFlowFactory _workFlowFactory;
        private readonly IRepository<WorkFlowCategeory, Guid> _workFlowCategeoryRepository;
        private readonly IWokFlowLogStore _wokFlowLogService;
        private readonly IWorkFlowProcessStore _workFlowProcessStore;
        private readonly IWorkFlowFormDataStore _workFlowFormDataStore;
        private readonly IWorkFlowSuperManager _workFlowSuperManager;
        private readonly IRepository<WorkFlowProcess, Guid> _workFlowProcessRepository;
        private readonly IRepository<WorkFlowPaperData, Guid> _workFlowPaperDataRepository;

        public ArchiveAppService(
            IRepository<WorkFlowPaperArchive, Guid> workFlowPaperArchiveRepository,
            IWorkFlowFactory workFlowFactory,
            IRepository<WorkFlowCategeory, Guid> workFlowCategeoryRepository,
            IWokFlowLogStore wokFlowLogService,
            IWorkFlowProcessStore workFlowProcessStore,
            IWorkFlowFormDataStore workFlowFormDataStore,
            IWorkFlowSuperManager workFlowSuperManager,
            IRepository<WorkFlowProcess, Guid> workFlowProcessRepository,
            IRepository<WorkFlowPaperData, Guid> workFlowPaperDataRepository)
        {
            _workFlowPaperArchiveRepository = workFlowPaperArchiveRepository;
            _workFlowFactory = workFlowFactory;
            _workFlowCategeoryRepository = workFlowCategeoryRepository;
            _wokFlowLogService = wokFlowLogService;
            _workFlowProcessStore = workFlowProcessStore;
            _workFlowFormDataStore = workFlowFormDataStore;

            _workFlowSuperManager = workFlowSuperManager;
            _workFlowProcessRepository = workFlowProcessRepository;
            _workFlowPaperDataRepository = workFlowPaperDataRepository;
        }


        public async Task<PagedResultDto<PagedArchivedPaperDto>> GetPagedPapersAsync(PagedArchivedPaperInput input)
        {
            IQueryable<PagedArchivedPaperDto> query = null;
            var archiveQuery = _workFlowPaperArchiveRepository.GetAll()
                   .WhereIf(input.StartDate != null, t => t.CreationTime >= input.StartDate.Value)
                   .WhereIf(input.EndDate != null, t => t.CreationTime <= input.EndDate.Value)
                   .WhereIf(!input.UserName.IsNullOrEmpty(), t => t.UserName == input.UserName)
                   .WhereIf(!input.UnitName.IsNullOrEmpty(), t => t.UnitName == input.UnitName)
                   .WhereIf(input.WorkFlowId != null, t => t.WorkFlowId == input.WorkFlowId)
                   .WhereIf(input.UnitId > 0, t => t.UnitId == input.UnitId)
                   .WhereIf(input.ApprovalResult != null, item => item.ApprovalResult == input.ApprovalResult)
                   .WhereIf(input.WenHaoId != null,t=>t.WenHaoId == input.WenHaoId.Value)
                   .WhereIf(input.NodeId != null, t=> t.NodeId == input.NodeId.Value)
                   .WhereIf(!input.ExclusiveWorkFlowId.IsNullOrEmpty(), t => !input.ExclusiveWorkFlowId.Contains(t.WorkFlowId));

            if (IsGranted(WorkFlowPermissions.Pages_Manage_WorkFlow_GuiDang_Query))
            {
                query = from s in archiveQuery
                        select new PagedArchivedPaperDto
                        {
                            SerialNumber = s.SerialNumber,
                            CreationTime = s.CreationTime,
                            PaperId = s.Id,
                            ReferNumber = s.ReferNumber,
                            Title = s.Title,
                            UnitId = s.UnitId,
                            UnitName = s.UnitName,
                            UserId = s.CreatorUserId,
                            UserName = s.UserName,
                            WorkFlowId = s.WorkFlowId,
                            WhName = s.WhName,
                            WhYear = s.WhYear,
                            WhNumber = s.WhNumber,
                            WenHaoId = s.WenHaoId,
                            ApprovalResult = s.ApprovalResult,
                            NodeId = s.NodeId,
                            PublishTime = s.RegistrationTime
                        };
            }
            else
            {
                var currentUserId = AbpSession.UserId;
                query = from s in archiveQuery
                        join 
                        pp in 
                        from d in _workFlowProcessRepository.GetAll().Where(item=>item.UserId == currentUserId)
                        group 1 by d.PaperId into tt
                        select new {PaperId  = tt.Key}
                        on s.Id equals pp.PaperId 
                        select new PagedArchivedPaperDto
                        {
                            SerialNumber = s.SerialNumber,
                            CreationTime = s.CreationTime,
                            PaperId = s.Id,
                            ReferNumber = s.ReferNumber,
                            Title = s.Title,
                            UnitId = s.UnitId,
                            UnitName = s.UnitName,
                            UserId = s.CreatorUserId,
                            UserName = s.UserName,
                            WorkFlowId = s.WorkFlowId,
                            WhName = s.WhName,
                            WhYear = s.WhYear,
                            WhNumber = s.WhNumber,
                            WenHaoId = s.WenHaoId,
                            ApprovalResult = s.ApprovalResult,
                            NodeId = s.NodeId,
                            PublishTime = s.RegistrationTime
                        };
            }
            

            if (!input.Filter.IsNullOrEmpty())
            {
                query = query.Where(t => t.Title.Contains(input.Filter) || t.ReferNumber.Contains(input.Filter));
            }

            var count = await query.CountAsync();

            query = !string.IsNullOrWhiteSpace(input.Sorting)
                  ? query.OrderBy(input.Sorting).PageBy(input)
                  : query.OrderByDescending(t => t.CreationTime).PageBy(input);
            
            //Logger.Debug(query.ToSql());

            var list = await query.ToListAsync();

            await GetOtherData(input, list);

            return new PagedResultDto<PagedArchivedPaperDto>(count, list);
        }

        private async Task GetOtherData(PagedArchivedPaperInput input, List<PagedArchivedPaperDto> list)
        {
            var paperIds = list.Select(item => item.PaperId).ToList();

            //var psjDatas = await _workFlowPiShiJianService.GetPsDataAsync(paperIds);

            var fieldList = new List<string>();
            if (!input.FieldList.IsNullOrEmpty())
            {
                fieldList = input.FieldList
                    .Where((c, index) => index < 10)
                    .ToList();
            }

            fieldList = fieldList.Select(c => c.ToPascalCase()).ToList();

            var paperDatas = await _workFlowPaperDataRepository
                .GetAllListAsync(item => paperIds.Contains(item.PaperId) && fieldList.Contains(item.FieldName));

            foreach (var item in list)
            {
                //var psList = (psjDatas != null && psjDatas.ContainsKey(item.PaperId) ? psjDatas[item.PaperId] : new List<WorkFlowPiShiJianDto>());
                //item.Title = psList.Count > 0 ? item.Title + "（批示件）" : item.Title;

                var wf = await GetWorkFlowDefinitionAsync(item.WorkFlowId);
                item.WorkFlowName = wf.Definition.Name;
                item.NodeName = wf.FindNode(item.NodeId).Definition.Name;

                var e = paperDatas.FindAll(n => n.PaperId == item.PaperId);
                foreach (var appendField in fieldList)
                {
                    if (item.FieldList == null)
                    {
                        item.FieldList = new Dictionary<string, string>();
                    }
                    item.FieldList.Add(
                        appendField.ToCamelCase(),
                        e.FirstOrDefault(f => f.FieldName == appendField)?.Value
                    );
                }
            }
        }

        //[AbpAuthorize(WorkFlowPermissions.Pages_Manage_WorkFlow_Admin)]
        //public async Task<int> ArchiveAsync(ArchiveInput input)
        //{
        //    var list = await _workFlowPaperRepository.GetAllListAsync(c => c.CreationTime <= input.Date && c.PaperState == PaperState.Finished);
        //    if(list.Count > 0)
        //    {
        //        foreach (var paper in list)
        //        {
        //            var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
        //            using (var diyfunction = DiyFunctionsHelper.GetWorkFlowDiyFunction(workFlowDefinition.Definition.OptionFunction, _iocResolver))
        //            {
        //                await diyfunction.Object.BeforeArchivedAsync(paper);
        //                await AddToArchiveTableAsync(paper);

        //                Guid id = paper.Id;
        //                await _workFlowPaperRepository.DeleteAsync(id);

        //                await diyfunction.Object.AfterArchivedAsync(id);
        //            }
        //        }
        //    }

        //    return list.Count;
        //}

        //[AbpAuthorize(WorkFlowPermissions.Pages_Manage_WorkFlow_Admin)]
        //public async Task<string> ArchiveSingleAsync(PaperIdInput input)
        //{
        //    var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == input.PaperId);
        //    if (paper != null)
        //    {
        //        if (paper.PaperState == PaperState.Finished)
        //        {
        //            var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
        //            using (var diyfunction = DiyFunctionsHelper.GetWorkFlowDiyFunction(workFlowDefinition.Definition.OptionFunction, _iocResolver))
        //            {
        //                await diyfunction.Object.BeforeArchivedAsync(paper);

        //                await AddToArchiveTableAsync(paper);
        //                await _workFlowPaperRepository.DeleteAsync(input.PaperId);

        //                await diyfunction.Object.AfterArchivedAsync(input.PaperId);
        //            }
        //            return "归档成功！";
        //        }
        //        else
        //        {
        //            return "该文档尚未办结！";
        //        }
        //    }
        //    else
        //    {
        //        return $"未找到id为{input.PaperId}的稿件";
        //    }
        //}

        [AbpAuthorize(WorkFlowPermissions.Pages_Manage_WorkFlow_Admin)]
        public async Task DeleteAsync(PaperIdInput input)
        {
            await _workFlowSuperManager.DeleteArchiveAsync(input.PaperId);
        }

        private async Task AddToArchiveTableAsync(WorkFlowPaper paper)
        {
            var newEntity = ObjectMapper.Map<WorkFlowPaperArchive>(paper);
            await _workFlowPaperArchiveRepository.InsertAsync(newEntity);
        }

        public async Task<List<NameValue>> GetWorkFlowListAsync()
        {
            var categoryList = await _workFlowCategeoryRepository.GetAllListAsync(c => c.IsActive);
            var worflowList = await _workFlowFactory.GetAllWorkFlowDefinitionAsync();
            worflowList = worflowList
                .Where(c => c.Definition.NodeList.Length > 0)
                .ToList();

            var result = categoryList.OrderBy(c => c.Order)
                         .SelectMany(c => {
                             var wfList = worflowList.Where(wf => wf.Definition.CategeoryId == c.Id).OrderBy(wf => wf.Definition.Order);
                             return wfList.Select(cc => new NameValue($"{c.Name}-{cc.Definition.Name}", cc.Definition.WorkFlowId.ToString()));
                         })
                         .ToList();
            return result;
        }

        public async Task<FormDataOutput> GetPaperInfoAsync(PaperIdInput input)
        {
            var paper = await _workFlowPaperArchiveRepository.FirstOrDefaultAsync(input.PaperId);
            if(paper == null)
            {
                return null;
            }
            var workflow = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

            var remarkData = await _workFlowProcessStore.GetAllProcessDataAsync(paper.Id);
            foreach (var item in remarkData)
            {
                item.NodeName = workflow.FindNode(item.NodeId).Definition.Name;
                item.UserInfo = item.UserInfoDtoJson.FromJsonString<UserInfoDto>();
                item.UserInfoDtoJson = "";
            }
            remarkData = remarkData.OrderBy(c => c.StepCode).ThenBy(c => c.Code).ThenBy(c => c.CreationTime)
                .ThenByDescending(c => c.UserInfo.Order).ToList();

            return new FormDataOutput
            {
                Paper = ObjectMapper.Map<WorkFlowPaperDto>(paper),
                RemarkData = remarkData,
                LogData = await _wokFlowLogService.GetLogDataAsync(paper.Id),
                FormData = await _workFlowFormDataStore.GetFormDataForReadOnlyAsync(paper.Id, workflow, paper.NodeId, paper.CreatorUserId.Value),
                FormHtml = workflow.Definition.FormHtml,
                Now = Clock.Now,
                IsHaveOnlineWord = workflow.Definition.IsHaveOnlineWord,
                CanEditAttachment = false,
                IsHasAttachment = workflow.Definition.IsHaveAttachment,
                WfUniqueName = workflow.Definition.CustomData?.UniqueName,
                AdditionalMsg = await _workFlowProcessStore.GetAllAdditionalMsgAsync(paper.Id),
                Relationships = await _workFlowProcessStore.GetWorkFlowRelationshipsAsync(paper.Id)
            };
        }

        public async Task<FormDataOutput> GetDataForPrintAsync(PaperIdInput input)
        {
            return await GetPaperInfoAsync(input);
        }

        private async Task<IWorkflowDefinition> GetWorkFlowDefinitionAsync(Guid workFlowId)
        {
            return await _workFlowFactory.GetWorkflowDefinitionAsync(workFlowId);
        }
    }
}
